using System;
using System.Collections.Generic;
using System.Text;

namespace SubSonic
{
    internal class InsertSetting
    {
        internal string ColumnName = "";
        internal string ParameterName = "";
        internal object Value;
        internal bool IsExpression = false;
    }

    public class Insert
    {
        internal List<InsertSetting> Inserts = new List<InsertSetting>();
        internal SqlQuery SelectValues = null;
        public DataProvider provider = null;
        internal TableSchema.Table Table;
        internal string[] SelectColumnList = new string[0];
        internal string SelectColumns = "";

        public Insert() {}

        public Insert(string tableName)
        {
            TableSchema.Table tbl = DataService.GetSchema(tableName, "");
            Init(tbl);
        }

        public Insert(string tableName, string providerName)
        {
            TableSchema.Table tbl = DataService.GetSchema(tableName, providerName);
            Init(tbl);
        }

        public Insert(TableSchema.Table tbl)
        {
            Table = tbl;
        }

        public Insert Into(TableSchema.Table tbl, params string[] columns)
        {
            SelectColumnList = columns;
            return Init(tbl);
        }

        public Insert Into(TableSchema.Table tbl)
        {
            return Init(tbl);
        }

        private Insert Init(TableSchema.Table tbl)
        {
            if(tbl == null)
                throw new SqlQueryException("Can't find the table schema - please specify the provider if there is more than one, or check the spelling");

            if(SelectColumnList.Length == 0)
            {
                string columnList = tbl.GetDelimitedColumnList(",", false, true);
                SelectColumnList = columnList.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
                SelectColumns = columnList;
            }
            else
            {
                bool isFirst = true;
                StringBuilder sb = new StringBuilder();
                foreach(string s in SelectColumnList)
                {
                    if(!isFirst)
                        sb.Append(",");
                    sb.Append(s);

                    isFirst = false;
                }
                SelectColumns = sb.ToString();
            }

            if(Table == null)
                Table = tbl;

            provider = tbl.Provider;
            return this;
        }

        public Insert Values(params object[] values)
        {
            //this is a lineup game
            //make sure that the count of values
            //is equal to the columns
            if(values.Length != SelectColumnList.Length)
                throw new SqlQueryException("The Select list and value list don't match - they need to match exactly if you're creating an INSERT VALUES query");

            int itemIndex = 0;
            foreach(string s in SelectColumnList)
            {
                InsertSetting setting = new InsertSetting();
                setting.ColumnName = s;
                setting.ParameterName = Utilities.Utility.PrefixParameter("ins_" + s, provider);
                setting.Value = values[itemIndex];

                Inserts.Add(setting);

                itemIndex++;
            }

            return this;
        }

        public Insert ValueExpression(params object[] values)
        {
            //this is a lineup game
            //make sure that the count of values
            //is equal to the columns
            if(values.Length != SelectColumnList.Length)
                throw new SqlQueryException("The Select list and value list don't match - they need to match exactly if you're creating an INSERT VALUES query");

            int itemIndex = 0;
            foreach(string s in SelectColumnList)
            {
                InsertSetting setting = new InsertSetting();
                setting.ColumnName = s;
                setting.ParameterName = Utilities.Utility.PrefixParameter("ins_" + s, provider);
                setting.Value = values[itemIndex];
                setting.IsExpression = true;
                Inserts.Add(setting);

                itemIndex++;
            }

            return this;
        }

        public Insert Select(SqlQuery selectResults)
        {
            //validate the query
            //hard-set the select list
            //most of the time we don't want to insert into the PK
            if(selectResults.SelectColumnList.Length == 0)
            {
                bool omitPK = selectResults.FromTables[0].PrimaryKey.AutoIncrement;

                string columns = selectResults.FromTables[0].GetDelimitedColumnList(",", false, omitPK);
                selectResults.SelectColumnList = columns.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
            }

            SelectValues = selectResults;
            return this;
        }


        #region Execution

        public int Execute()
        {
            int result;
            string sql = BuildSqlStatement();
            QueryCommand cmd = new QueryCommand(sql, this.provider.Name);

            //add in the commands
            foreach(InsertSetting s in Inserts)
                cmd.Parameters.Add(s.ParameterName, s.Value);

            //set the contstraints, if we're using a Select statement
            if(Inserts.Count == 0 && SelectValues != null)
                SqlQuery.SetConstraintParams(SelectValues, cmd);

            try
            {
                result = DataService.ExecuteQuery(cmd);
            }
            catch(Exception x)
            {
                SqlQueryException ex = new SqlQueryException(x.Message);
                throw ex;
            }
            return result;
        }

        #endregion


        public string BuildSqlStatement()
        {
            SqlQuery q = new SqlQuery();
            ISqlGenerator generator = q.GetGenerator();
            generator.SetInsertQuery(this);

            string sql = generator.BuildInsertStatement();
            return sql;
        }

        public override string ToString()
        {
            return BuildSqlStatement();
        }
    }
}